﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection;
using MugenInjection.Bindings;
using MugenInjection.Core;
using MugenInjection.Exceptions;
using MugenInjection.Interface;
using MugenInjection.Parameters;
using MugenInjection.Infrastructure;
using Should;

namespace MugenInjectionTest.Core
{
    [TestClass]
    public partial class InjectorTest : TestBase
    {
        [TestMethod]
        public void BindAndBindWithManualBuildTest()
        {
            using (MugenInjector injector = GetInjector())
            {
                injector.Bind(typeof(IDisposable)).ToConstant(injector);
                injector.ShouldEqual(injector.Get<IDisposable>());
            }

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<IDisposable>().ToConstant(injector);
                injector.ShouldEqual(injector.Get<IDisposable>());
            }

            using (MugenInjector injector = GetInjector())
            {
                var syntax = injector.BindWithManualBuild(typeof(IDisposable)).ToConstant(injector);
                TestUtils.Catch<BindingNotFoundException>(() => injector.Get<IDisposable>());
                syntax.Build();
                injector.ShouldEqual(injector.Get<IDisposable>());
            }

            using (MugenInjector injector = GetInjector())
            {
                var syntax = injector.BindWithManualBuild<IDisposable>().ToConstant(injector);
                TestUtils.Catch<BindingNotFoundException>(() => injector.Get<IDisposable>());
                syntax.Build();
                injector.ShouldEqual(injector.Get<IDisposable>());
            }
        }

        [TestMethod]
        public void DisposeTest()
        {
            MugenInjector injector = GetInjector();
            injector.IsDisposed.ShouldBeFalse();
            injector.Dispose();
            injector.IsDisposed.ShouldBeTrue();
            IBindingContext bindingContext = new BindingContext(ServiceType, TypeInto, ParameterInfo, injector, injector,
                                                                new IInjectionParameter[0],
                                                                new Dictionary<string, object>());
            TestUtils.Catch<ObjectDisposedException>(() => injector.CreateChild());
            TestUtils.Catch<ObjectDisposedException>(
                () =>
                injector.AddBinding(new ConstantBinding("", new[] { typeof(object) }, null, context => true)));
            TestUtils.Catch<ObjectDisposedException>(
                () =>
                injector.RemoveBinding(new ConstantBinding("", new[] { typeof(object) }, null, context => true)));
            TestUtils.Catch<ObjectDisposedException>(() => injector.GetBindings(typeof(object)));
            TestUtils.Catch<ObjectDisposedException>(() => injector.GetParent());
            TestUtils.Catch<ObjectDisposedException>(() => injector.GetRoot());
            TestUtils.Catch<ObjectDisposedException>(() => injector.Inject(ref injector));
            TestUtils.Catch<ObjectDisposedException>(() => injector.Load(new List<IInjectorModule> { new TestModule() }));
            TestUtils.Catch<ObjectDisposedException>(() => injector.Load(new TestModule()));
            TestUtils.Catch<ObjectDisposedException>(() => injector.ModuleIsLoaded("test"));
            TestUtils.Catch<ObjectDisposedException>(() => injector.ModuleIsLoaded(typeof(TestModule)));
            TestUtils.Catch<ObjectDisposedException>(
                () =>
                injector.Release(typeof(object)));
            TestUtils.Catch<ObjectDisposedException>(() => injector.ReleaseAll(typeof(object), true));
            TestUtils.Catch<ObjectDisposedException>(() => injector.Unload("test"));
            TestUtils.Catch<ObjectDisposedException>(() => injector.Unload(typeof(TestModule)));
            TestUtils.Catch<ObjectDisposedException>(
                () =>
                injector.Resolve(bindingContext));

            TestUtils.Catch<ObjectDisposedException>(
                () =>
                injector.Resolve(bindingContext));

            TestUtils.Catch<ObjectDisposedException>(
                () =>
                injector.CanResolve(bindingContext, true, true));
        }

        [TestMethod]
        public void ReleaseAllTest()
        {
            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf().InSingletonScope();
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto<string>().InSingletonScope();

                var releaseDisposed = injector.Get<ReleaseDisposed>();
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeof(string).GetTypeInfo());
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseAll(typeof(ReleaseDisposed), false);

                var afterReleaseDisposed = injector.Get<ReleaseDisposed>();
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeof(string).GetTypeInfo());
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldNotEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseAll(typeof(ReleaseDisposed), true);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeTrue();

                releaseDisposed = injector.Get<ReleaseDisposed>();
                releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeof(string).GetTypeInfo());
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseTest()
        {
            var typeInto = typeof(string).GetTypeInfo();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf().InSingletonScope();
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.Get<ReleaseDisposed>();
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), false);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.Get<ReleaseDisposed>();
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), true);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseWithParametersTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            IInjectionParameter parameter = new PropertyParameter("rs", typeInto.AsType(), "");

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf().When(binding => binding.Parameters.Contains(parameter)).
                    InSingletonScope();
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.Get<ReleaseDisposed>(parameter);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), false, null, parameter);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.Get<ReleaseDisposed>(parameter);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), true, null, parameter);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseWithSpecialParametersTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var dictionary = new Dictionary<string, object> { { "1", "" } };

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf().When(binding => binding.SpecialParameters.ContainsKey("1")).InSingletonScope();
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.Get<ReleaseDisposed>(dictionary);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), false, dictionary);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();


                var afterReleaseDisposed = injector.Get<ReleaseDisposed>(dictionary);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), true, dictionary);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseWithSpecialParametersAndParametersTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            IInjectionParameter parameter = new PropertyParameter("rs", typeInto.AsType(), "");
            var dictionary = new Dictionary<string, object> { { "1", "" } };

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .When(
                        binding => binding.SpecialParameters.ContainsKey("1") && binding.Parameters.Contains(parameter))
                    .InSingletonScope();
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.Get<ReleaseDisposed>(dictionary, parameter);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), false, dictionary, parameter);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.Get<ReleaseDisposed>(dictionary, parameter);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), true, dictionary, parameter);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseWithSpecialParametersAndParametersAndNamedTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            IInjectionParameter parameter = new PropertyParameter("rs", typeInto.AsType(), "");
            var dictionary = new Dictionary<string, object> { { "1", "" } };

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .When(
                        binding => binding.SpecialParameters.ContainsKey("1") && binding.Parameters.Contains(parameter))
                    .InSingletonScope()
                    .NamedBinding("name");
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.Get<ReleaseDisposed>("name", dictionary, parameter);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), "name", false, dictionary, parameter);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.Get<ReleaseDisposed>("name", dictionary, parameter);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), "name", true, dictionary, parameter);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseWithSpecialParametersAndNamedTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var dictionary = new Dictionary<string, object> { { "1", "" } };

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .When(binding => binding.SpecialParameters.ContainsKey("1"))
                    .InSingletonScope()
                    .NamedBinding("name");
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.Get<ReleaseDisposed>("name", dictionary);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), "name", false, dictionary);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.Get<ReleaseDisposed>("name", dictionary);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), "name", true, dictionary);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseWithParametersAndNamedTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            IInjectionParameter parameter = new PropertyParameter("rs", typeInto.AsType(), "");

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .When(binding => binding.Parameters.Contains(parameter))
                    .InSingletonScope()
                    .NamedBinding("name");
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.Get<ReleaseDisposed>("name", null, parameter);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), "name", false, null, parameter);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.Get<ReleaseDisposed>("name", null, parameter);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), "name", true, null, parameter);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseNamedTest()
        {
            var typeInto = typeof(string).GetTypeInfo();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .InSingletonScope()
                    .NamedBinding("name");
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.Get<ReleaseDisposed>("name");
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), "name", false);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.Get<ReleaseDisposed>("name");
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.Release(typeof(ReleaseDisposed), "name", true);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseIntoTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var typeIntoDisposed = typeof(Attribute).GetTypeInfo();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeIntoDisposed.AsType()).InSingletonScope();
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.GetInto<ReleaseDisposed>(typeIntoDisposed);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), typeIntoDisposed, null, false);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.GetInto<ReleaseDisposed>(typeIntoDisposed);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), typeIntoDisposed, null, true);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseIntoWithParametersTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var typeIntoDisposed = typeof(Attribute).GetTypeInfo();
            IInjectionParameter parameter = new PropertyParameter("rs", typeInto.AsType(), "");

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf().When(
                    binding => binding.Parameters.Contains(parameter) && binding.TypeInto == typeIntoDisposed.AsType()).
                    InSingletonScope();
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.GetInto<ReleaseDisposed>(typeIntoDisposed, null, null, parameter);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), typeIntoDisposed, null, false, null, parameter);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.GetInto<ReleaseDisposed>(typeIntoDisposed, null, null, parameter);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), typeIntoDisposed, null, true, null, parameter);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseIntoWithSpecialParametersTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var typeIntoDisposed = typeof(Attribute).GetTypeInfo();
            var dictionary = new Dictionary<string, object> { { "1", "" } };

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf().When(
                    binding => binding.SpecialParameters.ContainsKey("1") && binding.TypeInto == typeIntoDisposed.AsType()).
                    InSingletonScope();
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.GetInto<ReleaseDisposed>(typeIntoDisposed, null, dictionary);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), typeIntoDisposed, null, false, dictionary);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.GetInto<ReleaseDisposed>(typeIntoDisposed, null, dictionary);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), typeIntoDisposed, null, true, dictionary);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseIntoWithSpecialParametersAndParametersTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var typeIntoDisposed = typeof(Attribute).GetTypeInfo();
            IInjectionParameter parameter = new PropertyParameter("rs", typeInto.AsType(), "");
            var dictionary = new Dictionary<string, object> { { "1", "" } };

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .When(
                        binding =>
                        binding.SpecialParameters.ContainsKey("1") && binding.Parameters.Contains(parameter) &&
                        binding.TypeInto == typeIntoDisposed.AsType())
                    .InSingletonScope();
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.GetInto<ReleaseDisposed>(typeIntoDisposed, null, dictionary, parameter);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), typeIntoDisposed, null, false, dictionary, parameter);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.GetInto<ReleaseDisposed>(typeIntoDisposed, null, dictionary, parameter);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), typeIntoDisposed, null, true, dictionary, parameter);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseIntoWithSpecialParametersAndParametersAndNamedTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var typeIntoDisposed = typeof(Attribute).GetTypeInfo();
            IInjectionParameter parameter = new PropertyParameter("rs", typeInto.AsType(), "");
            var dictionary = new Dictionary<string, object> { { "1", "" } };

            //simple with special parameter and parameter and named
            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .When(
                        binding =>
                        binding.SpecialParameters.ContainsKey("1") && binding.Parameters.Contains(parameter) &&
                        binding.TypeInto == typeIntoDisposed.AsType())
                    .InSingletonScope()
                    .NamedBinding("name");
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.GetInto<ReleaseDisposed>("name", typeIntoDisposed, null, dictionary, parameter);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), "name", typeIntoDisposed, null, false, dictionary, parameter);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.GetInto<ReleaseDisposed>("name", typeIntoDisposed, null, dictionary,
                                                                             parameter);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), "name", typeIntoDisposed, null, true, dictionary, parameter);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseIntoWithSpecialParametersAndNamedTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var typeIntoDisposed = typeof(Attribute).GetTypeInfo();
            var dictionary = new Dictionary<string, object> { { "1", "" } };

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .When(binding => binding.SpecialParameters.ContainsKey("1") && binding.TypeInto == typeIntoDisposed.AsType())
                    .InSingletonScope()
                    .NamedBinding("name");
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.GetInto<ReleaseDisposed>("name", typeIntoDisposed, null, dictionary);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), "name", typeIntoDisposed, null, false, dictionary);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.GetInto<ReleaseDisposed>("name", typeIntoDisposed, null, dictionary);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), "name", typeIntoDisposed, null, true, dictionary);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseIntoWithParametersAndNamedTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var typeIntoDisposed = typeof(Attribute).GetTypeInfo();
            IInjectionParameter parameter = new PropertyParameter("rs", typeInto.AsType(), "");

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .When(binding => binding.Parameters.Contains(parameter) && binding.TypeInto == typeIntoDisposed.AsType())
                    .InSingletonScope()
                    .NamedBinding("name");
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.GetInto<ReleaseDisposed>("name", typeIntoDisposed, null, null, parameter);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), "name", typeIntoDisposed, null, false, null, parameter);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.GetInto<ReleaseDisposed>("name", typeIntoDisposed, null, null, parameter);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), "name", typeIntoDisposed, null, true, null, parameter);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        [TestMethod]
        public void ReleaseIntoNamedTest()
        {
            var typeInto = typeof(string).GetTypeInfo();
            var typeIntoDisposed = typeof(Attribute).GetTypeInfo();

            using (MugenInjector injector = GetInjector())
            {
                injector.Bind<ReleaseDisposed>().ToSelf()
                    .WhenInto(typeIntoDisposed.AsType())
                    .InSingletonScope()
                    .NamedBinding("name");
                injector.Bind<ReleaseDisposed>().ToSelf().WhenInto(typeInto.AsType()).InSingletonScope();

                var releaseDisposed = injector.GetInto<ReleaseDisposed>("name", typeIntoDisposed);
                var releaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), "name", typeIntoDisposed, null, false);

                releaseDisposed.IsDisposed.ShouldBeFalse();
                releaseDisposedInto.IsDisposed.ShouldBeFalse();

                var afterReleaseDisposed = injector.GetInto<ReleaseDisposed>("name", typeIntoDisposed);
                var afterReleaseDisposedInto = injector.GetInto<ReleaseDisposed>(typeInto);
                afterReleaseDisposed.ShouldNotEqual(releaseDisposed);
                afterReleaseDisposedInto.ShouldEqual(releaseDisposedInto);
                afterReleaseDisposed.IsDisposed.ShouldBeFalse();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();

                injector.ReleaseInto(typeof(ReleaseDisposed), "name", typeIntoDisposed, null, true);

                afterReleaseDisposed.IsDisposed.ShouldBeTrue();
                afterReleaseDisposedInto.IsDisposed.ShouldBeFalse();
            }
        }

        #region Nested type: ReleaseDisposed

        public class ReleaseDisposed : IDisposable
        {
            #region Property

            public bool IsDisposed { get; private set; }

            #endregion

            #region Implementation of IDisposable

            /// <summary>
            /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
            /// </summary>
            /// <filterpriority>2</filterpriority>
            public void Dispose()
            {
                IsDisposed = true;
            }

            #endregion
        }

        #endregion
    }
}